package entity

import (
	"database/sql"
	"github.com/astaxie/beego/orm"
	"github.com/gomodule/redigo/redis"
	"golang.org/x/crypto/bcrypt"
	"nspel/nspel"
	"strconv"
	"time"
)

var ManagerAuthority Authority

type Manager struct {
	Id        int64     `orm:"pk;auto;column(id)"`
	UserName  string    `orm:"column(user_name)"`
	Password  string    `orm:"column(password)"`
	Authority byte      `orm:"column(authority)"`
	CreatedAt time.Time `orm:"auto_now_add;type(datetime);column(created_at)"`
	UpdatedAt time.Time `orm:"auto_now;type(datetime);column(updated_at)"`
}

type ManagerList struct {
	Id                     int64
	UserName               string
	Authority              byte
	AreaProvinceId         int64
	AreaCityId             int64
	AreaName               string
	AreaContact            string
	SchoolProvinceId       int64
	SchoolCityId           int64
	SchoolExperimentAreaId int64
	SchoolName             string
	SchoolContact          string
}

type Count struct {
	Total int64
}

func init() {
	orm.RegisterModel(new(Manager))
	ManagerAuthority = Authority{Administrator: 0, Province: 8, City: 16, Area: 32, School: 64}
}

func (*Manager) TableName() string {
	return "manager"
}

func (manager *Manager) SelectAllByUserName(userName string) error {
	manager.UserName = userName
	key := "manager"
	hashKey := "userName[\"" + userName + "\"]"
	err := nspel.RedisHgetJson(key, hashKey, manager)
	if err == nil {
		return nil
	}
	if err != redis.ErrNil {
		nspel.Error(err)
	}
	err = nspel.GetOrmEngine().Read(manager, "user_name")
	if err != nil {
		return err
	}
	nspel.RedisHsetJson(key, hashKey, manager)
	return nil
}

func (manager *Manager) SelectAllById(id int64) error {
	manager.Id = id
	key := "manager"
	hashKey := "id[" + strconv.FormatInt(id, 10) + "]"
	err := nspel.RedisHgetJson(key, hashKey, manager)
	if err == nil {
		return nil
	}
	if err != redis.ErrNil {
		nspel.Error(err)
	}
	err = nspel.GetOrmEngine().Read(manager)
	if err != nil {
		return err
	}
	nspel.RedisHsetJson(key, hashKey, manager)
	return nil
}

func (manager *Manager) DeleteById(id int64) error {
	manager.Id = id
	err := manager.Delete()
	return err
}

func (manager *Manager) Insert() error {
	_, err := nspel.GetOrmEngine().Insert(manager)
	return err
}

func (manager *Manager) Delete() error {
	_, err := nspel.GetOrmEngine().Delete(manager)
	return err
}

type Authority struct {
	Administrator byte
	Province      byte
	City          byte
	Area          byte
	School        byte
}

func (*Manager) Register(userName string, password string, authority byte) (sql.NullInt64, error) {
	manager := new(Manager)
	manager.UserName = userName
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return sql.NullInt64{Int64: 0, Valid: false}, err
	}
	manager.Password = string(hashedPassword)
	manager.Authority = authority
	err = manager.Insert()
	if err != nil {
		return sql.NullInt64{Int64: 0, Valid: false}, err
	}
	return sql.NullInt64{Int64: int64(manager.Id), Valid: true}, nil
}

func (*Manager) SelectManagerListByUserNameContactPhonePageSizeCurrentPage(userName string, contactPhone string, pageSize int64, currentPage int64) (count int64, managerLists []ManagerList, err error) {
	key := "manager"
	hashKey := "userName[" + userName + "].contactPhone[" + contactPhone + "].pageSize[" + strconv.FormatInt(pageSize, 10) + "].currentPage[" + strconv.FormatInt(currentPage, 10) + "]"
	err = nspel.RedisHgetJson(key, hashKey+".list", &managerLists)
	if err != redis.ErrNil && err != nil {
		nspel.Error(err)
	} else if err != nil {
		rawSql := `SELECT
	manager.id AS id,
	manager.user_name AS user_name,
	manager.authority AS authority,
	area.province_id AS area_province_id,
	area.city_id AS area_city_id,
	area.NAME AS area_name,
	area.contact AS area_contact,
	school.province_id AS school_province_id,
	school.city_id AS school_city_id,
	school.experiment_area_id AS school_experiment_area_id,
	school.NAME AS school_name,
	school.contact AS school_contact 
FROM
	manager
	LEFT JOIN area ON manager.id = area.user_id
	LEFT JOIN school ON manager.id = school.user_id 
WHERE
	(
		( manager.authority = 32 AND area.id IS NOT NULL ) 
		OR ( manager.authority = 64 AND school.id IS NOT NULL ) 
		OR ( manager.authority = 0 ) 
	) `
		if len(userName) != 0 {
			rawSql += "AND manager.user_name like '%" + userName + "%' "
		}
		if len(contactPhone) != 0 {
			rawSql += "AND ( school.contact_phone like '%" + contactPhone + "%' OR area.contact_phone like '%" + contactPhone + "%') "
		}
		rawSql += "LIMIT " + strconv.FormatInt(pageSize, 10) + " OFFSET " + strconv.FormatInt((currentPage-1)*pageSize, 10)
		_, err = nspel.GetOrmEngine().Raw(rawSql).QueryRows(&managerLists)
		if err != nil || len(managerLists) == 0 {
			return
		}
		nspel.RedisHsetJson(key, hashKey+".list", managerLists)
	}
	hashKey = "userName[" + userName + "].contactPhone[" + contactPhone + "]"
	count, err = nspel.RedisHgetInt64(key, hashKey+".count")
	if err == nil {
		return
	}
	if err != redis.ErrNil {
		nspel.Error(err)
	}
	rawSql := `SELECT
	COUNT(*) as total
FROM
	manager
	LEFT JOIN area ON manager.id = area.user_id
	LEFT JOIN school ON manager.id = school.user_id 
WHERE
	(
		( manager.authority = 32 AND area.id IS NOT NULL ) 
		OR ( manager.authority = 64 AND school.id IS NOT NULL ) 
		OR ( manager.authority = 0 ) 
	) `
	if len(userName) != 0 {
		rawSql += "AND manager.user_name like '%" + userName + "%' "
	}
	if len(contactPhone) != 0 {
		rawSql += "AND ( school.contact_phone like '%" + contactPhone + "%' OR area.contact_phone like '%" + contactPhone + "%') "
	}
	var total Count
	err = nspel.GetOrmEngine().Raw(rawSql).QueryRow(&total)
	if err != nil {
		return
	}
	count = total.Total
	nspel.RedisHsetInt64(key, hashKey+".count", count)
	return
}

func (m *Manager) ChangePassword(id int64, password string) (err error) {
	m.Id = id
	passwordByte, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	m.Password = string(passwordByte)
	_, err = nspel.GetOrmEngine().Update(m, "password")
	return err
}

func (m *Manager) Update(columns ...string) (err error) {
	_, err = nspel.GetOrmEngine().Update(m, columns...)
	return err
}
